#include "LuaUtils.h"
#include "AbstractObject.h"
#include "Logger.h"
#include "InterData.h"

const char* getSafeLuaString(lua_State* L, int pos)
{
	const char* temp = lua_tostring(L, pos);
	if(temp == NULL)
		return "";
	else
		return temp;
}

AbstractObject* unboxAO(lua_State* L, int pos)
{
	// first, check if what we have is a table
	if(!lua_istable(L, pos))
	{
		LOG(logERROR) << "Cannot convert to AO*: not table.";
		return NULL;
	}
	else
	{
		lua_getfield(L, pos, "__sp");
		if(lua_isnil(L, -1))
		{
			LOG(logERROR) << "Table has no __sp! Cannot conver to AO*";
			lua_settop(L, -2);
			return NULL;
		}
		else
		{
			AnyAOPtr& temp = *((AnyAOPtr*)lua_touserdata(L, -1));
			AbstractObject* ret = *temp;
			lua_settop(L, -2);
			return ret;
		}
	}
}

AbstractObject* typeCheckUnboxAO(lua_State* L, int pos, const std::string& tname)
{
	AbstractObject* ao = unboxAO(L, pos);
	if(ao == NULL)
		return ao;
	else
	{
		if(ao->getCppTypeName() == tname)
			return ao;
		else
		{
			LOG(logERROR) << "AO* unboxing type error: expected " << tname << ", got " << ao->getCppTypeName();
			return NULL;
		}
	}
}

AnyAOPtr unboxAOPtr(lua_State* L, int pos)
{
	return AnyAOPtr(unboxAO(L, pos));
}

AnyAOPtr unboxAOPtrSafe(lua_State* L, int pos, const std::string& tname)
{
	return AnyAOPtr(typeCheckUnboxAO(L, pos, tname));
}

void pushIData(lua_State* L, IData& idata)
{
	lua_newtable(L);
	if(idata.size() == 0)
		return;

	std::string tempstr;
	for(int i = 0; i < idata.size(); ++i)
	{
		std::string curName = idata.getname(i);
		if(curName != "")
		{
			switch(idata.getType(i))
			{
			case IV_INT:
				lua_pushnumber(L, idata.geti(i));
				break;
			case IV_FLOAT:
				lua_pushnumber(L, idata.getf(i));
				break;
			case IV_UCHAR:
				lua_pushnumber(L, idata.getu(i));
				break;
			case IV_STRING:
				//if(curName.length() <= 2 || curName.substr(curName.length() - 2, curName.length()) != "_x")
				//	lua_pushstring(L, idata.gets(i).c_str());
				//else // push an 'unsafe' string which can contain any bytes whatsoever
				tempstr = idata.gets(i);
				lua_pushlstring(L, tempstr.data(), tempstr.length());
				break;
			default:
				lua_pushnil(L);
				break;
			}
			lua_setfield(L, -2, curName.c_str());
		}
	}
}

IData popIData(lua_State* L, int pos)
{
	IData ret;
	/* table is in the stack at index 'pos' */
	lua_pushnil(L);  /* first key */
	while (lua_next(L, pos) != 0) 
	{
		// key is index -2
		// value is index -1
		if(lua_type(L,-2) == LUA_TSTRING)
		{
			std::string keyname(getSafeLuaString(L, -2));
			std::string keySuffix = "";
			if(keyname.length() > 2)
				keySuffix = keyname.substr(keyname.length() - 2, keyname.length());
			std::string temps;
			float tempf;
			size_t tempi;
			int tempii;
			unsigned char tempu;
			const char* tempcp;
			switch(lua_type(L, -1))
			{
			case LUA_TSTRING:
				//if(keySuffix == "_x")
				//{
				tempcp = lua_tolstring(L, -1, &tempi);
				temps = std::string(tempcp, tempi);
				//}
				//else
				//	temps = getSafeLuaString(L, -1);

				ret.sets(ret.named(keyname), temps);
				break;
			case LUA_TNUMBER:
				if(keySuffix == "_i")
				{
					tempii = (int)lua_tointeger(L, -1);
					ret.seti(ret.named(keyname), tempii);
				}
				else if(keySuffix == "_u")
				{
					tempu = (unsigned char)lua_tointeger(L, -1);
					ret.setu(ret.named(keyname), tempu);
				}
				else
				{
					tempf = (float)lua_tonumber(L, -1);
					ret.setf(ret.named(keyname), tempf);
				}
				break;
			case LUA_TBOOLEAN:
				tempf = (float)lua_tonumber(L, -1);
				ret.setf(ret.named(keyname), tempf);
				break;
			default:
				break;
			}
		}
		/* removes 'value'; keeps 'key' for next iteration */
		lua_pop(L, 1);
	}
	return ret;
}